package stats

import (
	"fmt"
	"github.com/go-redis/redis"
	"go.uber.org/zap"
	redis2 "longmen/server/config/global"
	"longmen/server/internal/services/consumers/publisher"
	"longmen/server/pkg/db/mysql/models/models_main"
	cache2 "longmen/server/pkg/db/redis/cache"
	"strconv"
	"sync"
	"time"
)

var statsMap = map[publisher.StatType]string{
	publisher.UsageStatistic:    "usageStatistic",    // 打开
	publisher.LoginStatistic:    "loginStatistic",    // userInfo登录
	publisher.LoginApiStatistic: "loginApiStatistic", // 登录接口
	publisher.RegisterStatistic: "registerStatistic", // 注册
}

var instance *Statistic
var once sync.Once

func NewStatistic(log *zap.SugaredLogger) *Statistic {
	once.Do(func() {
		rds := redis2.GetRds()
		instance = &Statistic{
			rds:    rds,
			logger: log,
		}
	})
	return instance
}

type Statistic struct {
	rds    redis.UniversalClient
	logger *zap.SugaredLogger
}

func contains(s []int, i int) bool {
	for _, v := range s {
		if v == i {
			return true
		}
	}

	return false
}

// Incr 叠加Unique
func (s Statistic) Incr(req publisher.PublishStats) {
	var (
		storeVal, key string
	)
	switch req.StatType {
	case publisher.UsageStatistic:
		storeVal = fmt.Sprintf("%d;%s;%s", req.PlatForm, req.ClientIp, req.DeviceUUID)
		// 安卓和iOS的按照设备ID去重
		if contains(publisher.MobileDeviceGroup, int(req.PlatForm)) {
			key = req.DeviceUUID
		} else {
			// web和h5的按照IP地址去重
			key = req.ClientIp
		}
		s.logger.Debugf("UsageStatistic PlatForm：%v,userId:%v", req.PlatForm, req.UserId)
	case publisher.LoginStatistic:
		storeVal = fmt.Sprintf("%d;%s;%s", req.PlatForm, req.ClientIp, req.DeviceUUID)
		key = strconv.Itoa(req.UserId)
	case publisher.LoginApiStatistic:
		storeVal = fmt.Sprintf("%d;%s;%s", req.PlatForm, req.ClientIp, req.DeviceUUID)
		key = strconv.Itoa(req.UserId)
	case publisher.RegisterStatistic:
		storeVal = fmt.Sprintf("%d;%s;%s", req.PlatForm, req.ClientIp, req.DeviceUUID)
		key = strconv.Itoa(req.UserId)
	}
	ok := s.setCacheMap(s.getMapKey(req.StatType, ""), key, storeVal)
	if req.UserId != 0 {
		cache2.UpdateLoginPlatformCache(req.UserId, int(req.PlatForm))
	}
	if ok {
		s.setCacheIncr(s.getIncrKey(req.StatType, ""), strconv.Itoa(int(req.PlatForm)))
	}
}

func (s Statistic) getIncrKey(tt publisher.StatType, date string) string {
	keyType, ok := statsMap[tt]
	if !ok {
		keyType = "unknown"
	}
	if date == "" || len(date) == 0 {
		date = time.Now().Format("20060102")
	}
	return "incr_" + keyType + "_" + date
}
func (s Statistic) setCacheIncr(key, plat string) {
	err := s.rds.HIncrBy(key, plat, 1).Err()
	if err != nil {
		s.logger.Errorf("set Statistic incr  error: %v", err)
		return
	}
}

func (s Statistic) getMapKey(tt publisher.StatType, date string) string {
	keyType, ok := statsMap[tt]
	if !ok {
		keyType = "unknown"
	}
	if date == "" || len(date) == 0 {
		date = time.Now().Format("20060102")
	}
	return "map_" + keyType + "_" + date
}

func (s Statistic) setCacheMap(key, field string, val interface{}) bool {
	nx, err := s.rds.HSetNX(key, field, val).Result()
	if err != nil {
		s.logger.Errorf("set Statistic incr  error: %v", err)
		return false
	}
	return nx
}

func (s Statistic) stringToInt(str string) int {
	atoi, err := strconv.Atoi(str)
	if err != nil {
		return 0
	}
	return atoi
}

func (s Statistic) GetToDatabase(date string) []*models_main.UserStatistics {
	records := make([]*models_main.UserStatistics, 0)
	keyMap := map[publisher.StatType]string{
		publisher.LoginStatistic:    s.getIncrKey(publisher.LoginStatistic, date),
		publisher.RegisterStatistic: s.getIncrKey(publisher.RegisterStatistic, date),
		publisher.UsageStatistic:    s.getIncrKey(publisher.UsageStatistic, date),
	}

	for sType, key := range keyMap {
		toModels := s.getToModels(key, date, uint8(sType))
		if toModels == nil {
			continue
		}
		records = append(records, toModels)
	}
	return records
}
func (s Statistic) getToModels(key, date string, statType uint8) *models_main.UserStatistics {
	result, err := s.rds.HGetAll(key).Result()
	if err != nil {
		return nil
	}
	return &models_main.UserStatistics{
		StatType: statType,
		Android:  s.stringToInt(result[strconv.Itoa(publisher.Android)]),
		Ios:      s.stringToInt(result[strconv.Itoa(publisher.IOS)]),
		Web:      s.stringToInt(result[strconv.Itoa(publisher.WEB)]),
		H5:       s.stringToInt(result[strconv.Itoa(publisher.H5)]),
		Date:     date,
	}
}

func (s Statistic) DeleteCache(date string) {
	deleteType := []publisher.StatType{
		publisher.UsageStatistic,
		publisher.LoginStatistic,
		publisher.RegisterStatistic,
		publisher.LoginApiStatistic,
	}

	keyMap := make([]string, 0)

	for _, statType := range deleteType {
		keyMap = append(keyMap, s.getMapKey(statType, date))
		keyMap = append(keyMap, s.getIncrKey(statType, date))
	}

	for _, key := range keyMap {
		err := s.rds.Del(key).Err()
		if err != nil {
			s.logger.Errorf("err: %v", err)
		}
	}
	//return records
}
